@ctrl-ui/react 0.8.17 → 0.8.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blocks/coding-agent.js +1 -1
- package/dist/blocks/design-canvas-inspector.js +1 -1
- package/dist/blocks/design-canvas.js +9 -5
- package/dist/blocks/discussion-markdown-input.d.ts +12 -0
- package/dist/blocks/discussion-markdown-input.js +34 -0
- package/dist/blocks/discussion.d.ts +7 -3
- package/dist/blocks/discussion.js +77 -16
- package/dist/blocks/file-explorer.js +4 -4
- package/dist/blocks/settings.js +5 -3
- package/dist/knob-contracts/label-knobs.d.ts +4 -0
- package/dist/knob-contracts/label-knobs.js +4 -0
- package/dist/knob-contracts/markdown-editor-knobs.d.ts +4 -0
- package/dist/knob-contracts/markdown-editor-knobs.js +16 -0
- package/dist/knob-contracts/switch-knobs.d.ts +1 -1
- package/dist/knob-contracts/switch-knobs.js +1 -0
- package/dist/markdown-editor/context.d.ts +22 -0
- package/dist/markdown-editor/context.js +10 -0
- package/dist/markdown-editor/extensions.d.ts +10 -0
- package/dist/markdown-editor/extensions.js +139 -0
- package/dist/markdown-editor/toolbar.d.ts +9 -0
- package/dist/markdown-editor/toolbar.js +336 -0
- package/dist/markdown-editor/uploads.d.ts +23 -0
- package/dist/markdown-editor/uploads.js +32 -0
- package/dist/markdown-editor/use-image-uploads.d.ts +12 -0
- package/dist/markdown-editor/use-image-uploads.js +120 -0
- package/dist/markdown-editor.d.ts +27 -0
- package/dist/markdown-editor.js +192 -0
- package/dist/styles/index.css +4 -2
- package/dist/styles/recipes/code.css +4 -0
- package/dist/styles/recipes/field.css +1 -1
- package/dist/styles/recipes/label.css +19 -0
- package/dist/styles/recipes/markdown-editor.css +196 -0
- package/dist/styles/recipes/switch.css +17 -5
- package/dist/styles/theme.css +25 -0
- package/dist/styles/type-scale.css +28 -2
- package/dist/ui/app-shell.js +1 -1
- package/dist/ui/autocomplete.js +1 -1
- package/dist/ui/code.js +1 -0
- package/dist/ui/combobox.js +1 -1
- package/dist/ui/command.js +1 -1
- package/dist/ui/dockable-panel.js +1 -1
- package/dist/ui/label.d.ts +8 -0
- package/dist/ui/label.js +14 -0
- package/dist/ui/markdown.d.ts +2 -1
- package/dist/ui/markdown.js +3 -3
- package/dist/ui/responsive-dialog.js +1 -1
- package/dist/ui/sidebar-menu.js +1 -1
- package/dist/ui/sidebar.js +1 -1
- package/dist/ui/typography.d.ts +44 -0
- package/dist/ui/typography.js +59 -0
- package/dist/ui/use-switch-drag.js +1 -1
- package/package.json +37 -1
|
@@ -215,7 +215,7 @@ function CodingAgentEmptyState({ title = "What should we build?", description, i
|
|
|
215
215
|
})
|
|
216
216
|
}),
|
|
217
217
|
/* @__PURE__ */ jsx("h2", {
|
|
218
|
-
className: "text-
|
|
218
|
+
className: "text-heading-3 sm:text-heading-2",
|
|
219
219
|
children: title
|
|
220
220
|
}),
|
|
221
221
|
description ? /* @__PURE__ */ jsx("p", {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Button } from "../ui/button.js";
|
|
3
3
|
import { InputGroup, InputGroupAddon } from "../ui/input-group.js";
|
|
4
|
-
import { resizeDesignCanvasLayer } from "./design-canvas-data.js";
|
|
5
4
|
import { NumberField, NumberFieldGroup, NumberFieldInput, NumberFieldScrubArea } from "../ui/number-field.js";
|
|
6
5
|
import { ColorPicker, ColorPickerArea, ColorPickerContent, ColorPickerEyeDropper, ColorPickerHue, ColorPickerInput, ColorPickerTrigger } from "../ui/color-picker.js";
|
|
7
6
|
import { Empty, EmptyDescription, EmptyHeader, EmptyTitle } from "../ui/empty.js";
|
|
8
7
|
import { Separator } from "../ui/separator.js";
|
|
9
8
|
import { Toggle } from "../ui/toggle.js";
|
|
9
|
+
import { resizeDesignCanvasLayer } from "./design-canvas-data.js";
|
|
10
10
|
import { useState } from "react";
|
|
11
11
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
import { BlendIcon, EyeIcon, EyeOffIcon, Link2Icon, MinusIcon, PlusIcon, RotateCwIcon, SquareRoundCornerIcon, Unlink2Icon } from "lucide-react";
|
|
@@ -3,12 +3,13 @@ import { cn } from "../lib/cn.js";
|
|
|
3
3
|
import { Button } from "../ui/button.js";
|
|
4
4
|
import { ScrollArea } from "../ui/scroll-area.js";
|
|
5
5
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip.js";
|
|
6
|
-
import {
|
|
7
|
-
import { DesignCanvasInspector } from "./design-canvas-inspector.js";
|
|
6
|
+
import { Toolbar, ToolbarButton, ToolbarGroup, ToolbarSeparator } from "../ui/toolbar.js";
|
|
8
7
|
import { Drawer, DrawerContent, DrawerTitle } from "../ui/drawer.js";
|
|
9
8
|
import { InfiniteCanvas, InfiniteCanvasContent, InfiniteCanvasControls, InfiniteCanvasItem } from "../ui/infinite-canvas.js";
|
|
10
9
|
import { ResizableFloatingPanel } from "../ui/resizable.js";
|
|
11
|
-
import {
|
|
10
|
+
import { Text } from "../ui/typography.js";
|
|
11
|
+
import { createDesignCanvasLayer, designCanvasFillColor, drawnDesignCanvasBounds } from "./design-canvas-data.js";
|
|
12
|
+
import { DesignCanvasInspector } from "./design-canvas-inspector.js";
|
|
12
13
|
import { useRef, useState } from "react";
|
|
13
14
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
15
|
import { CircleIcon, FrameIcon, LayersIcon, MousePointer2Icon, SlidersHorizontalIcon, SquareIcon, TypeIcon } from "lucide-react";
|
|
@@ -414,8 +415,11 @@ function DesignCanvasLayerNode({ layer, selected, onSelect, onKeyDown, onLayerCh
|
|
|
414
415
|
className: "absolute start-0 bottom-full mb-1 whitespace-nowrap text-caption text-muted-foreground",
|
|
415
416
|
children: layer.name
|
|
416
417
|
}) : null,
|
|
417
|
-
layer.kind === "text" ? /* @__PURE__ */ jsx(
|
|
418
|
-
|
|
418
|
+
layer.kind === "text" ? /* @__PURE__ */ jsx(Text, {
|
|
419
|
+
as: "p",
|
|
420
|
+
size: "heading-2",
|
|
421
|
+
weight: "normal",
|
|
422
|
+
className: "size-full overflow-hidden whitespace-nowrap",
|
|
419
423
|
style: { color: fillColor },
|
|
420
424
|
children: layer.text
|
|
421
425
|
}) : /* @__PURE__ */ jsx("div", {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { MarkdownImageUploader } from "../markdown-editor/uploads.js";
|
|
2
|
+
import { MarkdownEditorStatus } from "../markdown-editor.js";
|
|
3
|
+
//#region src/blocks/discussion-markdown-input.d.ts
|
|
4
|
+
export default function DiscussionMarkdownInput({ label, placeholder, autoFocus, onUploadImage, onStatusChange, blocked }: {
|
|
5
|
+
label: string;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
autoFocus?: boolean;
|
|
8
|
+
onUploadImage?: MarkdownImageUploader;
|
|
9
|
+
onStatusChange: (status: MarkdownEditorStatus) => void;
|
|
10
|
+
blocked: boolean;
|
|
11
|
+
}): import("react").JSX.Element;
|
|
12
|
+
//#endregion
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useChatComposerContext } from "../chat-composer.js";
|
|
3
|
+
import { MarkdownEditor, MarkdownEditorContent } from "../markdown-editor.js";
|
|
4
|
+
import { MarkdownEditorToolbar, MarkdownEditorUploads } from "../markdown-editor/toolbar.js";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/blocks/discussion-markdown-input.tsx
|
|
7
|
+
function DiscussionMarkdownInput({ label, placeholder, autoFocus, onUploadImage, onStatusChange, blocked }) {
|
|
8
|
+
const composer = useChatComposerContext();
|
|
9
|
+
return /* @__PURE__ */ jsxs(MarkdownEditor, {
|
|
10
|
+
value: composer.value,
|
|
11
|
+
onValueChange: composer.setValue,
|
|
12
|
+
disabled: composer.isDisabled || composer.isLocked,
|
|
13
|
+
onUploadImage,
|
|
14
|
+
onStatusChange,
|
|
15
|
+
onKeyDownCapture: (event) => {
|
|
16
|
+
if (!(event.target instanceof HTMLElement) || !event.target.isContentEditable && event.target.tagName !== "TEXTAREA") return;
|
|
17
|
+
if (event.key !== "Enter" || !(event.metaKey || event.ctrlKey) || event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return;
|
|
18
|
+
event.preventDefault();
|
|
19
|
+
event.stopPropagation();
|
|
20
|
+
if (!blocked) composer.submit();
|
|
21
|
+
},
|
|
22
|
+
children: [
|
|
23
|
+
/* @__PURE__ */ jsx(MarkdownEditorToolbar, {}),
|
|
24
|
+
/* @__PURE__ */ jsx(MarkdownEditorContent, {
|
|
25
|
+
label,
|
|
26
|
+
placeholder,
|
|
27
|
+
autoFocus
|
|
28
|
+
}),
|
|
29
|
+
/* @__PURE__ */ jsx(MarkdownEditorUploads, {})
|
|
30
|
+
]
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { DiscussionMarkdownInput as default };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MarkdownImageUploader } from "../markdown-editor/uploads.js";
|
|
1
2
|
import { ChatComposer } from "../chat-composer.js";
|
|
2
3
|
import { ChatMessage } from "../chat-message.js";
|
|
3
4
|
import { ComponentProps, ReactNode } from "react";
|
|
@@ -9,20 +10,23 @@ export type DiscussionComposerProps = Omit<ComponentProps<typeof ChatComposer>,
|
|
|
9
10
|
secondaryAction?: ReactNode;
|
|
10
11
|
hint?: ReactNode;
|
|
11
12
|
autoFocus?: boolean;
|
|
13
|
+
format?: "plain" | "markdown";
|
|
14
|
+
onUploadImage?: MarkdownImageUploader;
|
|
12
15
|
};
|
|
13
|
-
export declare function DiscussionComposer({ label, placeholder, submitLabel, secondaryAction, hint, autoFocus, ...props }: DiscussionComposerProps): import("react").JSX.Element;
|
|
16
|
+
export declare function DiscussionComposer({ label, placeholder, submitLabel, secondaryAction, hint, autoFocus, format, onUploadImage, onSubmit, ...props }: DiscussionComposerProps): import("react").JSX.Element;
|
|
14
17
|
export type DiscussionCommentProps = Omit<ComponentProps<typeof ChatMessage>, "from" | "layout" | "authorLabel" | "children"> & {
|
|
15
18
|
author: string;
|
|
16
19
|
avatar?: ReactNode;
|
|
17
20
|
sentAt: string;
|
|
18
21
|
timeLabel: ReactNode;
|
|
19
22
|
edited?: boolean;
|
|
20
|
-
children
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
markdown?: string;
|
|
21
25
|
actions?: ReactNode;
|
|
22
26
|
onReply?: () => void;
|
|
23
27
|
replyLabel?: ReactNode;
|
|
24
28
|
replies?: ReactNode;
|
|
25
29
|
replyComposer?: ReactNode;
|
|
26
30
|
};
|
|
27
|
-
export declare function DiscussionComment({ author, avatar, sentAt, timeLabel, edited, children, actions, onReply, replyLabel, replies, replyComposer, ...props }: DiscussionCommentProps): import("react").JSX.Element;
|
|
31
|
+
export declare function DiscussionComment({ author, avatar, sentAt, timeLabel, edited, children, markdown, actions, onReply, replyLabel, replies, replyComposer, ...props }: DiscussionCommentProps): import("react").JSX.Element;
|
|
28
32
|
//#endregion
|
|
@@ -3,32 +3,90 @@ import { Button } from "../ui/button.js";
|
|
|
3
3
|
import { ChatComposer, ChatComposerShell, ChatComposerSubmit, ChatComposerTextarea, ChatComposerToolbar } from "../chat-composer.js";
|
|
4
4
|
import { ChatMessageReplies } from "../chat-message/social.js";
|
|
5
5
|
import { ChatMessage, ChatMessageActions, ChatMessageAuthor, ChatMessageAvatar, ChatMessageBody, ChatMessageContent, ChatMessageFooter, ChatMessageHeader, ChatMessageRow, ChatMessageTime } from "../chat-message.js";
|
|
6
|
+
import { Markdown } from "../ui/markdown.js";
|
|
6
7
|
import { Kbd, KbdGroup } from "../ui/kbd.js";
|
|
7
|
-
import {
|
|
8
|
+
import { Suspense, lazy, useState } from "react";
|
|
9
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
10
|
import { CornerDownRightIcon } from "lucide-react";
|
|
9
11
|
//#region src/blocks/discussion.tsx
|
|
10
|
-
|
|
12
|
+
const DiscussionMarkdownInput = lazy(() => import("./discussion-markdown-input.js"));
|
|
13
|
+
function DiscussionComposer({ label, placeholder, submitLabel = "Post", secondaryAction, hint = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(KbdGroup, { children: [/* @__PURE__ */ jsx(Kbd, { children: "⌘" }), /* @__PURE__ */ jsx(Kbd, { children: "Enter" })] }), /* @__PURE__ */ jsx("span", { children: "to post" })] }), autoFocus, format = "plain", onUploadImage, onSubmit, ...props }) {
|
|
14
|
+
const [draft, setDraft] = useState(props.defaultValue ?? "");
|
|
15
|
+
const value = props.value ?? draft;
|
|
16
|
+
const [status, setStatus] = useState({
|
|
17
|
+
isEmpty: false,
|
|
18
|
+
hasPendingUploads: false
|
|
19
|
+
});
|
|
20
|
+
const [submitting, setSubmitting] = useState(false);
|
|
21
|
+
const [error, setError] = useState(null);
|
|
22
|
+
const blocked = format === "markdown" && (status.isEmpty || status.hasPendingUploads);
|
|
11
23
|
return /* @__PURE__ */ jsx(ChatComposer, {
|
|
12
24
|
submitKey: "mod-enter",
|
|
13
25
|
...props,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
value,
|
|
27
|
+
onValueChange: (next) => {
|
|
28
|
+
setDraft(next);
|
|
29
|
+
props.onValueChange?.(next);
|
|
30
|
+
},
|
|
31
|
+
state: submitting ? "submitting" : props.state,
|
|
32
|
+
onSubmit: async (payload) => {
|
|
33
|
+
if (blocked || submitting) return;
|
|
34
|
+
setSubmitting(true);
|
|
35
|
+
setError(null);
|
|
36
|
+
try {
|
|
37
|
+
await onSubmit?.({
|
|
38
|
+
...payload,
|
|
39
|
+
value: format === "markdown" ? value : payload.value
|
|
40
|
+
});
|
|
41
|
+
} catch (cause) {
|
|
42
|
+
setError(cause instanceof Error ? cause.message : "Couldn't post your comment. Please try again.");
|
|
43
|
+
} finally {
|
|
44
|
+
setSubmitting(false);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
children: /* @__PURE__ */ jsxs(ChatComposerShell, { children: [
|
|
48
|
+
format === "markdown" ? /* @__PURE__ */ jsx(Suspense, {
|
|
49
|
+
fallback: /* @__PURE__ */ jsx(ChatComposerTextarea, {
|
|
50
|
+
"aria-label": label,
|
|
51
|
+
placeholder,
|
|
52
|
+
disabled: true
|
|
53
|
+
}),
|
|
54
|
+
children: /* @__PURE__ */ jsx(DiscussionMarkdownInput, {
|
|
55
|
+
label,
|
|
56
|
+
placeholder,
|
|
57
|
+
autoFocus,
|
|
58
|
+
onUploadImage,
|
|
59
|
+
onStatusChange: setStatus,
|
|
60
|
+
blocked
|
|
61
|
+
})
|
|
62
|
+
}) : /* @__PURE__ */ jsx(ChatComposerTextarea, {
|
|
63
|
+
"aria-label": label,
|
|
64
|
+
placeholder,
|
|
65
|
+
autoFocus,
|
|
66
|
+
"aria-keyshortcuts": "Meta+Enter Control+Enter"
|
|
67
|
+
}),
|
|
68
|
+
error ? /* @__PURE__ */ jsx("p", {
|
|
69
|
+
role: "alert",
|
|
70
|
+
className: "px-3",
|
|
71
|
+
children: error
|
|
72
|
+
}) : null,
|
|
73
|
+
/* @__PURE__ */ jsxs(ChatComposerToolbar, { children: [/* @__PURE__ */ jsx("span", {
|
|
74
|
+
className: "flex min-w-0 items-center gap-1.5 text-caption text-muted-foreground",
|
|
75
|
+
children: hint
|
|
76
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
77
|
+
className: "flex items-center gap-2",
|
|
78
|
+
children: [secondaryAction, /* @__PURE__ */ jsx(ChatComposerSubmit, {
|
|
79
|
+
disabled: blocked || submitting,
|
|
80
|
+
children: submitLabel
|
|
81
|
+
})]
|
|
82
|
+
})] })
|
|
83
|
+
] })
|
|
26
84
|
});
|
|
27
85
|
}
|
|
28
86
|
function initials(name) {
|
|
29
87
|
return name.split(/\s+/).slice(0, 2).map((word) => word.charAt(0).toUpperCase()).join("");
|
|
30
88
|
}
|
|
31
|
-
function DiscussionComment({ author, avatar, sentAt, timeLabel, edited = false, children, actions, onReply, replyLabel = "Reply", replies, replyComposer, ...props }) {
|
|
89
|
+
function DiscussionComment({ author, avatar, sentAt, timeLabel, edited = false, children, markdown, actions, onReply, replyLabel = "Reply", replies, replyComposer, ...props }) {
|
|
32
90
|
const hasReplies = Boolean(replies) || Boolean(replyComposer);
|
|
33
91
|
return /* @__PURE__ */ jsx(ChatMessage, {
|
|
34
92
|
from: "participant",
|
|
@@ -49,7 +107,10 @@ function DiscussionComment({ author, avatar, sentAt, timeLabel, edited = false,
|
|
|
49
107
|
}),
|
|
50
108
|
edited ? /* @__PURE__ */ jsx("span", { children: "(edited)" }) : null
|
|
51
109
|
] }),
|
|
52
|
-
/* @__PURE__ */ jsx(ChatMessageContent, { children
|
|
110
|
+
/* @__PURE__ */ jsx(ChatMessageContent, { children: markdown !== void 0 ? /* @__PURE__ */ jsx(Markdown, {
|
|
111
|
+
content: markdown,
|
|
112
|
+
mode: "static"
|
|
113
|
+
}) : children }),
|
|
53
114
|
onReply ? /* @__PURE__ */ jsx(ChatMessageFooter, { children: /* @__PURE__ */ jsxs(Button, {
|
|
54
115
|
variant: "ghost",
|
|
55
116
|
size: "xs",
|
|
@@ -8,12 +8,12 @@ import { Input } from "../ui/input.js";
|
|
|
8
8
|
import { SidebarProvider, useSidebar } from "../ui/sidebar-provider.js";
|
|
9
9
|
import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "../ui/sidebar-menu.js";
|
|
10
10
|
import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarInset, SidebarRail, SidebarTrigger } from "../ui/sidebar.js";
|
|
11
|
+
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "../ui/breadcrumb.js";
|
|
11
12
|
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "../ui/empty.js";
|
|
12
13
|
import { Drawer, DrawerBody, DrawerContent, DrawerTitle, DrawerTrigger } from "../ui/drawer.js";
|
|
14
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "../ui/dropdown-menu.js";
|
|
13
15
|
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "../ui/resizable.js";
|
|
14
16
|
import { groupFileExplorerItems, orderFileExplorerEntries, resolveFileExplorer, searchFileExplorer } from "./file-explorer-data.js";
|
|
15
|
-
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "../ui/breadcrumb.js";
|
|
16
|
-
import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from "../ui/dropdown-menu.js";
|
|
17
17
|
import { Fragment, useEffect, useId, useRef, useState } from "react";
|
|
18
18
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
19
19
|
import { ChevronLeftIcon, ChevronRightIcon, FileIcon, FolderIcon, MoreHorizontalIcon, SearchIcon, XIcon } from "lucide-react";
|
|
@@ -395,7 +395,7 @@ function FileExplorerColumns({ columns, selectedEntry, selectedPath, headingAs,
|
|
|
395
395
|
}), /* @__PURE__ */ jsx(DrawerContent, { children: selectedEntry ? /* @__PURE__ */ jsx(DrawerBody, { children: /* @__PURE__ */ jsx(FileExplorerPreviewContent, {
|
|
396
396
|
entry: selectedEntry,
|
|
397
397
|
title: /* @__PURE__ */ jsx(DrawerTitle, {
|
|
398
|
-
className: "mt-4 max-w-full truncate text-heading-4
|
|
398
|
+
className: "mt-4 max-w-full truncate text-heading-4",
|
|
399
399
|
children: selectedEntry.name
|
|
400
400
|
})
|
|
401
401
|
}) }) : null })] })]
|
|
@@ -533,7 +533,7 @@ function FileExplorerPreview({ entry, headingAs: Heading }) {
|
|
|
533
533
|
entry,
|
|
534
534
|
className: "px-6 py-10",
|
|
535
535
|
title: /* @__PURE__ */ jsx(Heading, {
|
|
536
|
-
className: "mt-4 max-w-full truncate text-heading-4
|
|
536
|
+
className: "mt-4 max-w-full truncate text-heading-4",
|
|
537
537
|
title: entry.name,
|
|
538
538
|
children: entry.name
|
|
539
539
|
})
|
package/dist/blocks/settings.js
CHANGED
|
@@ -10,8 +10,9 @@ import { SidebarProvider, useSidebar } from "../ui/sidebar-provider.js";
|
|
|
10
10
|
import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "../ui/sidebar-menu.js";
|
|
11
11
|
import { Sidebar, SidebarContent, SidebarHeader, SidebarInset, SidebarTrigger } from "../ui/sidebar.js";
|
|
12
12
|
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "../ui/empty.js";
|
|
13
|
-
import { searchSettings } from "./settings-data.js";
|
|
14
13
|
import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet } from "../ui/field.js";
|
|
14
|
+
import { Heading } from "../ui/typography.js";
|
|
15
|
+
import { searchSettings } from "./settings-data.js";
|
|
15
16
|
import { useEffect, useId, useState } from "react";
|
|
16
17
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
17
18
|
import { ArrowLeftIcon, SearchIcon, XIcon } from "lucide-react";
|
|
@@ -212,8 +213,9 @@ function SettingsPage({ page, instanceId }) {
|
|
|
212
213
|
const { layout } = useSidebar();
|
|
213
214
|
return /* @__PURE__ */ jsxs("div", {
|
|
214
215
|
className: "mx-auto flex w-full max-w-4xl flex-col gap-10 px-5 py-8 md:px-10 md:py-12",
|
|
215
|
-
children: [/* @__PURE__ */ jsx(
|
|
216
|
-
|
|
216
|
+
children: [/* @__PURE__ */ jsx(Heading, {
|
|
217
|
+
level: layout === "contained" ? 2 : 1,
|
|
218
|
+
size: "heading-2",
|
|
217
219
|
children: page.title
|
|
218
220
|
}), page.sections.map((section) => /* @__PURE__ */ jsxs(FieldSet, {
|
|
219
221
|
className: "gap-3",
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
//#region src/knob-contracts/markdown-editor-knobs.d.ts
|
|
2
|
+
export declare const markdownEditorKnobs: readonly ["--cui-markdown-editor-background", "--cui-markdown-editor-foreground", "--cui-markdown-editor-border-color", "--cui-markdown-editor-radius", "--cui-markdown-editor-padding", "--cui-markdown-editor-min-height", "--cui-markdown-editor-max-height", "--cui-markdown-editor-gap", "--cui-markdown-editor-muted-foreground", "--cui-markdown-editor-code-background", "--cui-markdown-editor-border-width"];
|
|
3
|
+
export type MarkdownEditorKnobStyle = Partial<Record<(typeof markdownEditorKnobs)[number], string>>;
|
|
4
|
+
//#endregion
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/knob-contracts/markdown-editor-knobs.ts
|
|
2
|
+
const markdownEditorKnobs = [
|
|
3
|
+
"--cui-markdown-editor-background",
|
|
4
|
+
"--cui-markdown-editor-foreground",
|
|
5
|
+
"--cui-markdown-editor-border-color",
|
|
6
|
+
"--cui-markdown-editor-radius",
|
|
7
|
+
"--cui-markdown-editor-padding",
|
|
8
|
+
"--cui-markdown-editor-min-height",
|
|
9
|
+
"--cui-markdown-editor-max-height",
|
|
10
|
+
"--cui-markdown-editor-gap",
|
|
11
|
+
"--cui-markdown-editor-muted-foreground",
|
|
12
|
+
"--cui-markdown-editor-code-background",
|
|
13
|
+
"--cui-markdown-editor-border-width"
|
|
14
|
+
];
|
|
15
|
+
//#endregion
|
|
16
|
+
export { markdownEditorKnobs };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
//#region src/knob-contracts/switch-knobs.d.ts
|
|
2
|
-
export declare const switchKnobs: readonly ["--cui-switch-thumb-shadow", "--cui-switch-radius", "--cui-switch-shadow", "--cui-switch-background", "--cui-switch-border-color", "--cui-switch-border-width", "--cui-switch-checked-border-color", "--cui-switch-hover-background", "--cui-switch-checked-background", "--cui-switch-checked-hover-background", "--cui-switch-thumb-radius", "--cui-switch-thumb-background", "--cui-switch-inline-size", "--cui-switch-height", "--cui-switch-padding", "--cui-switch-thumb-inline-size", "--cui-switch-thumb-press-scale", "--cui-switch-thumb-backdrop-filter"];
|
|
2
|
+
export declare const switchKnobs: readonly ["--cui-switch-thumb-shadow", "--cui-switch-radius", "--cui-switch-shadow", "--cui-switch-background", "--cui-switch-border-color", "--cui-switch-border-width", "--cui-switch-checked-border-color", "--cui-switch-hover-background", "--cui-switch-checked-background", "--cui-switch-checked-hover-background", "--cui-switch-thumb-radius", "--cui-switch-thumb-background", "--cui-switch-inline-size", "--cui-switch-height", "--cui-switch-padding", "--cui-switch-thumb-inline-size", "--cui-switch-thumb-press-inline-size", "--cui-switch-thumb-press-scale", "--cui-switch-thumb-backdrop-filter"];
|
|
3
3
|
export type SwitchKnobStyle = Partial<Record<(typeof switchKnobs)[number], string>>;
|
|
4
4
|
//#endregion
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { MarkdownImageUpload } from "./uploads.js";
|
|
2
|
+
import { Editor } from "@tiptap/core";
|
|
3
|
+
//#region src/markdown-editor/context.d.ts
|
|
4
|
+
export type MarkdownEditorContextValue = {
|
|
5
|
+
editor: Editor | null;
|
|
6
|
+
disabled: boolean;
|
|
7
|
+
source: boolean;
|
|
8
|
+
markdown: string;
|
|
9
|
+
sourceRequired: boolean;
|
|
10
|
+
setSource: (source: boolean) => void;
|
|
11
|
+
setMarkdown: (value: string) => void;
|
|
12
|
+
setContentAttributes: (attributes: Record<string, string>) => void;
|
|
13
|
+
canUpload: boolean;
|
|
14
|
+
uploads: MarkdownImageUpload[];
|
|
15
|
+
upload: (files: File[], position?: number) => void;
|
|
16
|
+
retryUpload: (id: string) => void;
|
|
17
|
+
removeUpload: (id: string) => void;
|
|
18
|
+
uploadError: string | null;
|
|
19
|
+
};
|
|
20
|
+
export declare const MarkdownEditorContext: import("react").Context<MarkdownEditorContextValue | null>;
|
|
21
|
+
export declare function useMarkdownEditor(): MarkdownEditorContextValue;
|
|
22
|
+
//#endregion
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
//#region src/markdown-editor/context.ts
|
|
3
|
+
const MarkdownEditorContext = createContext(null);
|
|
4
|
+
function useMarkdownEditor() {
|
|
5
|
+
const context = useContext(MarkdownEditorContext);
|
|
6
|
+
if (!context) throw new Error("Markdown editor parts require a MarkdownEditor parent.");
|
|
7
|
+
return context;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { MarkdownEditorContext, useMarkdownEditor };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { JSONContent, Node } from "@tiptap/core";
|
|
2
|
+
import { MarkdownManager } from "@tiptap/markdown";
|
|
3
|
+
//#region src/markdown-editor/extensions.d.ts
|
|
4
|
+
export declare function isEditorUrl(value: string, image?: boolean): boolean;
|
|
5
|
+
export declare const ImageUpload: Node<any, any>;
|
|
6
|
+
export declare function markdownExtensions(): (import("@tiptap/core").Extension<import("@tiptap/markdown").MarkdownExtensionOptions, import("@tiptap/markdown").MarkdownExtensionStorage> | import("@tiptap/core").Extension<import("@tiptap/starter-kit").StarterKitOptions, any> | import("@tiptap/core").Extension<import("@tiptap/extension-table").TableKitOptions, any> | Node<any, any> | Node<import("@tiptap/extension-image").ImageOptions, any> | Node<import("@tiptap/extension-paragraph").ParagraphOptions, any> | Node<import("@tiptap/extension-list").TaskItemOptions, any> | Node<import("@tiptap/extension-list").TaskListOptions, any>)[];
|
|
7
|
+
export declare function createMarkdownCodec(): MarkdownManager;
|
|
8
|
+
export declare function requiresMarkdownSource(markdown: string, codec: MarkdownManager): boolean;
|
|
9
|
+
export declare function hasMarkdownContent(document: JSONContent): boolean;
|
|
10
|
+
//#endregion
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { Node } from "@tiptap/core";
|
|
2
|
+
import Image from "@tiptap/extension-image";
|
|
3
|
+
import { TaskItem, TaskList } from "@tiptap/extension-list";
|
|
4
|
+
import Paragraph from "@tiptap/extension-paragraph";
|
|
5
|
+
import { TableKit } from "@tiptap/extension-table";
|
|
6
|
+
import { Markdown, MarkdownManager } from "@tiptap/markdown";
|
|
7
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
8
|
+
//#region src/markdown-editor/extensions.ts
|
|
9
|
+
function isEditorUrl(value, image = false) {
|
|
10
|
+
if (!value || /[\s\\]/.test(value) || Array.from(value).some((character) => character.charCodeAt(0) < 32)) return false;
|
|
11
|
+
if (value.startsWith("/") && !value.startsWith("//")) return true;
|
|
12
|
+
if (!image && value.startsWith("#")) return true;
|
|
13
|
+
try {
|
|
14
|
+
const url = new URL(value);
|
|
15
|
+
return [
|
|
16
|
+
"https:",
|
|
17
|
+
"http:",
|
|
18
|
+
...image ? [] : ["mailto:"]
|
|
19
|
+
].includes(url.protocol);
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const SafeImage = Image.extend({
|
|
25
|
+
renderMarkdown: (node) => {
|
|
26
|
+
const src = String(node.attrs?.src ?? "").replace(/[()]/g, (character) => `\\${character}`);
|
|
27
|
+
const alt = String(node.attrs?.alt ?? "").replace(/[\\[\]]/g, (character) => `\\${character}`);
|
|
28
|
+
const title = String(node.attrs?.title ?? "").replace(/[\\"]/g, (character) => `\\${character}`);
|
|
29
|
+
return title ? `` : ``;
|
|
30
|
+
},
|
|
31
|
+
addAttributes() {
|
|
32
|
+
return {
|
|
33
|
+
...this.parent?.(),
|
|
34
|
+
src: {
|
|
35
|
+
default: null,
|
|
36
|
+
parseHTML: (element) => {
|
|
37
|
+
const src = element.getAttribute("src") ?? "";
|
|
38
|
+
return isEditorUrl(src, true) ? src : null;
|
|
39
|
+
},
|
|
40
|
+
renderHTML: (attributes) => ({ src: isEditorUrl(attributes.src ?? "", true) ? attributes.src : void 0 })
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const MarkdownParagraph = Paragraph.extend({ parseMarkdown: (token, helpers) => {
|
|
46
|
+
const content = helpers.parseInline(token.tokens ?? []);
|
|
47
|
+
const first = content[0];
|
|
48
|
+
const empty = content.length === 1 && first?.type === "text" && [" ", "\xA0"].includes(first.text ?? "");
|
|
49
|
+
return helpers.createNode("paragraph", void 0, empty ? [] : content);
|
|
50
|
+
} });
|
|
51
|
+
const ImageUpload = Node.create({
|
|
52
|
+
name: "imageUpload",
|
|
53
|
+
group: "block",
|
|
54
|
+
atom: true,
|
|
55
|
+
selectable: true,
|
|
56
|
+
addAttributes: () => ({
|
|
57
|
+
id: { default: null },
|
|
58
|
+
name: { default: "Image" }
|
|
59
|
+
}),
|
|
60
|
+
renderHTML: ({ node }) => [
|
|
61
|
+
"div",
|
|
62
|
+
{
|
|
63
|
+
"data-image-upload": "",
|
|
64
|
+
role: "status"
|
|
65
|
+
},
|
|
66
|
+
`Image: ${node.attrs.name}`
|
|
67
|
+
],
|
|
68
|
+
renderMarkdown: () => ""
|
|
69
|
+
});
|
|
70
|
+
function markdownExtensions() {
|
|
71
|
+
return [
|
|
72
|
+
StarterKit.configure({
|
|
73
|
+
underline: false,
|
|
74
|
+
paragraph: false,
|
|
75
|
+
link: {
|
|
76
|
+
openOnClick: false,
|
|
77
|
+
isAllowedUri: (url) => isEditorUrl(url)
|
|
78
|
+
}
|
|
79
|
+
}),
|
|
80
|
+
MarkdownParagraph,
|
|
81
|
+
SafeImage.configure({
|
|
82
|
+
allowBase64: false,
|
|
83
|
+
inline: true
|
|
84
|
+
}),
|
|
85
|
+
TaskList,
|
|
86
|
+
TaskItem.configure({
|
|
87
|
+
nested: true,
|
|
88
|
+
HTMLAttributes: { "data-type": "taskItem" }
|
|
89
|
+
}),
|
|
90
|
+
TableKit.configure({ table: { resizable: false } }),
|
|
91
|
+
ImageUpload,
|
|
92
|
+
Markdown
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
function createMarkdownCodec() {
|
|
96
|
+
return new MarkdownManager({ extensions: markdownExtensions() });
|
|
97
|
+
}
|
|
98
|
+
const supportedTokens = /* @__PURE__ */ new Set([
|
|
99
|
+
"space",
|
|
100
|
+
"code",
|
|
101
|
+
"heading",
|
|
102
|
+
"hr",
|
|
103
|
+
"blockquote",
|
|
104
|
+
"list",
|
|
105
|
+
"list_item",
|
|
106
|
+
"paragraph",
|
|
107
|
+
"text",
|
|
108
|
+
"escape",
|
|
109
|
+
"link",
|
|
110
|
+
"image",
|
|
111
|
+
"strong",
|
|
112
|
+
"em",
|
|
113
|
+
"codespan",
|
|
114
|
+
"br",
|
|
115
|
+
"del",
|
|
116
|
+
"table",
|
|
117
|
+
"def",
|
|
118
|
+
"taskList",
|
|
119
|
+
"taskItem"
|
|
120
|
+
]);
|
|
121
|
+
function unsupportedToken(value) {
|
|
122
|
+
if (Array.isArray(value)) return value.some(unsupportedToken);
|
|
123
|
+
if (typeof value !== "object" || value === null) return false;
|
|
124
|
+
if ("type" in value && typeof value.type === "string") {
|
|
125
|
+
if (!supportedTokens.has(value.type)) return true;
|
|
126
|
+
if ((value.type === "image" || value.type === "link") && "href" in value && typeof value.href === "string" && !isEditorUrl(value.href, value.type === "image")) return true;
|
|
127
|
+
}
|
|
128
|
+
return Object.values(value).some(unsupportedToken);
|
|
129
|
+
}
|
|
130
|
+
function requiresMarkdownSource(markdown, codec) {
|
|
131
|
+
return unsupportedToken(codec.instance.lexer(markdown));
|
|
132
|
+
}
|
|
133
|
+
function hasMarkdownContent(document) {
|
|
134
|
+
if (document.type === "image") return true;
|
|
135
|
+
if (document.type === "text" && document.text?.trim()) return true;
|
|
136
|
+
return document.content?.some(hasMarkdownContent) ?? false;
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
export { ImageUpload, createMarkdownCodec, hasMarkdownContent, isEditorUrl, markdownExtensions, requiresMarkdownSource };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/markdown-editor/toolbar.d.ts
|
|
3
|
+
export declare function MarkdownEditorToolbar({ children }: {
|
|
4
|
+
children?: ReactNode;
|
|
5
|
+
}): import("react").JSX.Element;
|
|
6
|
+
export declare function MarkdownEditorLink(): import("react").JSX.Element;
|
|
7
|
+
export declare function MarkdownEditorImage(): import("react").JSX.Element;
|
|
8
|
+
export declare function MarkdownEditorUploads(): import("react").JSX.Element;
|
|
9
|
+
//#endregion
|